package org.lemma.number;

import static org.lemma.number.Natural.ZERO;
import org.lemma.number.Numeric.Sign;
import javax.annotation.Author;
import javax.annotation.Copyright;
import javax.annotation.Version;

/**
 * <p>
 * A <dfn>rational number</dfn> is a number which can be expressed as a ratio of two
 * {@linkplain Integer integers}.
 * </p>
 * <p>
 * Non-integer rational numbers (commonly called fractions) are usually written as the
 * vulgar fraction <i>a&frasl;b</i>, where. <i>a</i> is called the numerator, and <i>b</i>
 * the denominator, and <i>b</i> is not zero.
 * </p>
 * <div class="references">
 *  <cite>
 *  "Rational number." <i>Wikipedia, The Free Encyclopedia</i>.
 *  29 Dec 2007, 03:53 UTC. Wikimedia Foundation, Inc.  4 Jan 2008
 *  <a href="http://en.wikipedia.org/w/index.php?title=Rational_number&amp;oldid=180738313">
 *  http://en.wikipedia.org/w/index.php?title=Rational_number&amp;oldid=180738313</a>
 *  </cite>
 * </div>
 *
 * @author Chris Beatty [christopher.beatty@gmail.com]
 * @version 1.0.0
 */
@Version (
    major="1", minor="0", patch="0", 
    date="Jun 16, 2008 2:21:36 PM",
    authors = {
        @Author (name="Chris Beatty", email="christopher.beatty@gmail.com")
    }
) @Copyright 
public abstract class Rational extends Real {     
    
    /**
     * Unicode for the vulgar fraction "\" character
     */
    private static final char UNICODE_SLASH = '\u2044';
    
    /**
     * The numerator for this rational number
     */
    private Integer numerator = ZERO;
    
    /**
     * The denominator for this rational number
     */
    private Integer denominator = ZERO;
    
    /**
     * Default Class Constructor
     * Creates and initializes a new instance of {@code Rational}
     */
    protected Rational() {
        this(ZERO, ZERO);
    }
    
    /**
     * Class Constructor
     * Creates a new instance of {@code Rational} and initializes its
     * numerator and denominator
     * 
     * @param num The numerator for this {@code Rational} as an {@link Integer}
     * @param den The denominator for this {@code Rational} as an {@link Integer}
     */
    public Rational(Integer num, Integer den) {
        if(num == null || den == null) {
            throw new IllegalArgumentException("NULL value");
        } else if(den.equals(ZERO)) {
            throw new IllegalArgumentException("Denominator is ZERO");
        } else {
            this.numerator = num;
            this.denominator = den;
        }
    }  

    /**
     * {@inheritDoc}
     * 
     * This is the overall sign for the rational number determined by the 
     * signs of the numerator and denominator.
     * 
     * @return {@inheritDoc}
     */
    @Override
    public Sign sign() {
        Sign n = this.numerator().sign();
        Sign d = this.denominator().sign();
        if(n == Sign.None) {
            return d;
        } else if(n == Sign.Negative) {
            if(d == Sign.Negative) {
                return Sign.Positive;
            } else {
                return Sign.Negative;
            }
        } else if(n == Sign.Positive) {
            if(d == Sign.Negative) {
                return Sign.Negative;
            } else {
                return Sign.Positive;
            }
        } else {
            throw new IllegalArgumentException("Unknown enum: " + n);
        }
    } 
    
    /**
     * Accessor method for the {@code numerator} property
     * 
     * @return Returns the {@code numerator} as an {@link Integer}
     */
    public Integer numerator() {
        return this.numerator;
    }
    
    /**
     * Accessor method for the {@code denominator} property
     * 
     * @return Returns the {@code denominator} as an {@link Integer}
     */    
    public Integer denominator() {
        return this.denominator;
    }    
    
    /**
     * {@inheritDoc}
     * 
     * @return {@inheritDoc}
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(this.numerator());
        sb.append(UNICODE_SLASH);
        sb.append(this.denominator());
        return sb.toString();
    }

}
